package com.tian.service.impl.pay;

import com.alibaba.fastjson.JSON;
import com.tian.common.util.RedisConstantPre;
import com.tian.entity.UserBalance;
import com.tian.entity.UserBalanceChangeRecord;
import com.tian.mapper.UserBalanceChangeRecordMapper;
import com.tian.mapper.UserBalanceMapper;
import com.tian.mq.message.UserBalanceDecrMessage;
import com.tian.mq.message.UserBalanceIncrMessage;
import com.tian.service.pay.UserBalanceService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBucket;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;

/**
 * {@code @description:} 用户余额增加服务实现类
 *
 * @author tianwc 公众号：Java后端技术全栈
 * 在线刷题 1200+java面试题和1000+篇技术文章：<a href="https://woaijava.cc/">博客地址</a>
 * {@code @date:} 2024-11-25 17:25
 * {@code @version:} 1.0
 */
@Slf4j
@Service
public class UserBalanceServiceImpl implements UserBalanceService {
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private UserBalanceMapper userBalanceMapper;
    @Resource
    private UserBalanceChangeRecordMapper userBalanceChangeRecordMapper;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void increaseUserBalance(UserBalanceIncrMessage userBalanceIncrMessage) {
        Long userId = userBalanceIncrMessage.getUserId();


        UserBalanceChangeRecord userBalanceChangeRecord = userBalanceChangeRecordMapper.selectByOrderNo(userBalanceIncrMessage.getReqId());
        if (userBalanceChangeRecord != null) {
            log.info("重复消息，orderNo:{}", userBalanceIncrMessage.getReqId());
            return;
        }

        //-----计算额度

        RLock lock = redissonClient.getLock(RedisConstantPre.USER_INFO_ID_LOCK_PRE + userId);
        lock.lock();
        try {
            UserBalance userBalance = userBalanceMapper.selectByUserId(userId);
            BigDecimal changeBalance = userBalanceIncrMessage.getChangeBalance();
            if (userBalance == null) {
                log.info("用户不存在，执行初始化，userId:{}", userId);
                userBalance = new UserBalance();
                userBalance.setUserId(userId);
                userBalance.setBalance(changeBalance.compareTo(BigDecimal.ZERO) > 0 ? userBalanceIncrMessage.getChangeBalance() : BigDecimal.ZERO);
                userBalance.setFreezeBalance(BigDecimal.ZERO);
                userBalance.setCreateTime(new Date());
                userBalanceMapper.insert(userBalance);
                if (changeBalance.compareTo(BigDecimal.ZERO) > 0) {
                    userBalanceChangeRecordMapper.insert(getUserBalanceChangeRecord(userBalanceIncrMessage, BigDecimal.ZERO, changeBalance));
                }
                cacheUserBalance(userBalance);
                return;
            }
            BigDecimal beforeBalance = userBalance.getBalance();
            BigDecimal afterBalance = beforeBalance.add(changeBalance);
            userBalance.setBalance(afterBalance);
            userBalanceMapper.updateByPrimaryKey(userBalance);
            userBalanceChangeRecordMapper.insert(getUserBalanceChangeRecord(userBalanceIncrMessage, beforeBalance, afterBalance));
            cacheUserBalance(userBalance);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public Boolean decreaseUserBalance(UserBalanceDecrMessage userBalanceDecrMessage) {
        UserBalanceChangeRecord userBalanceChangeRecord = userBalanceChangeRecordMapper.selectByOrderNo(userBalanceDecrMessage.getReqId());
        if (userBalanceChangeRecord != null) {
            log.info("重复消息，orderNo:{}", userBalanceDecrMessage.getReqId());
            return false;
        }
        RLock lock = redissonClient.getLock(RedisConstantPre.USER_INFO_ID_LOCK_PRE + userBalanceDecrMessage.getUserId());
        lock.lock();
        try {
            UserBalance userBalance = userBalanceMapper.selectByPrimaryKey(userBalanceDecrMessage.getUserId());
            if (userBalanceDecrMessage.getIsFreeze() && userBalance.getBalance().compareTo(userBalanceDecrMessage.getChangeBalance()) > 0) {
                userBalance.setFreezeBalance(userBalance.getFreezeBalance());
                userBalanceMapper.updateByPrimaryKey(userBalance);
                cacheUserBalance(userBalance);
                return true;
            }
            BigDecimal changeBalance = userBalanceDecrMessage.getChangeBalance();
            BigDecimal beforeBalance = userBalance.getBalance();
            BigDecimal afterBalance = beforeBalance.subtract(changeBalance);
            userBalance.setFreezeBalance(userBalance.getFreezeBalance().subtract(changeBalance));
            userBalance.setBalance(afterBalance);
            userBalanceMapper.updateByPrimaryKey(userBalance);

            userBalanceChangeRecord = new UserBalanceChangeRecord();
            userBalanceChangeRecord.setUserId(userBalanceDecrMessage.getUserId());
            userBalanceChangeRecord.setOrderNo(userBalanceDecrMessage.getReqId());
            userBalanceChangeRecord.setChangeType(userBalanceDecrMessage.getChangeType());
            userBalanceChangeRecord.setBeforeBalance(beforeBalance);
            userBalanceChangeRecord.setChangeBalance(userBalanceDecrMessage.getChangeBalance());
            userBalanceChangeRecord.setAfterBalance(afterBalance);
            userBalanceChangeRecord.setCreateTime(new Date());
            userBalanceChangeRecord.setRemark(userBalanceDecrMessage.getRemark());
            userBalanceChangeRecordMapper.insert(userBalanceChangeRecord);
            cacheUserBalance(userBalance);
        } finally {
            lock.unlock();
        }
        return true;
    }

    private void cacheUserBalance(UserBalance userBalance) {
        RBucket<String> bucket = redissonClient.getBucket(RedisConstantPre.USER_BALANCE_CACHE_PRE + userBalance.getUserId());
        bucket.set(JSON.toJSONString(userBalance));
    }

    private static UserBalanceChangeRecord getUserBalanceChangeRecord(UserBalanceIncrMessage userBalanceDecrMessage, BigDecimal beforeBalance, BigDecimal afterBalance) {
        UserBalanceChangeRecord userBalanceChangeRecord = new UserBalanceChangeRecord();
        userBalanceChangeRecord.setUserId(userBalanceDecrMessage.getUserId());
        userBalanceChangeRecord.setOrderNo(userBalanceDecrMessage.getReqId());
        userBalanceChangeRecord.setChangeType(userBalanceDecrMessage.getChangeType());
        userBalanceChangeRecord.setBeforeBalance(beforeBalance);
        userBalanceChangeRecord.setChangeBalance(userBalanceDecrMessage.getChangeBalance());
        userBalanceChangeRecord.setAfterBalance(afterBalance);
        userBalanceChangeRecord.setCreateTime(new Date());
        userBalanceChangeRecord.setRemark(userBalanceDecrMessage.getRemark());
        return userBalanceChangeRecord;
    }
}
